package com.jacob.androidstudy.androidnote.contentprovider;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.Map;

import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.os.Bundle;
import android.os.Environment;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.widget.SimpleCursorAdapter;
import android.widget.SlidingDrawer;
import android.database.sqlite.SQLiteOpenHelper;


/**
 * DataStorage
 * Android提供了以下几种方式来永久存储程序的数据，还提供了ContentProvider组件，将自己程序中的私有数据暴露给其他程序。
 * SQLiteDatabases - 在私有数据库中存储数据。
 * SharedPreferences - 使用键值对的方式存储简单的私有数据（通常是程序设置、个人喜好等）。系统会将数据存放在XML文件中。
 * InternalStorage - 在设备内部存储器中存储私有数据。
 * ExternalStorage - 在设备外部存储器中存储公用数据。
 * NetworkConnection - 将数据存储到自己的互联网服务器上。
 * 
 *
 */
public class DataStorage数据存储 extends PreferenceActivity {
	
	/**
	 * SQLiteDatabases
	 * SQLite是一种轻量级的、开源的、关系型数据库引擎，支持表、视图、主外键关联、索引、触发器等特性。
	 * Android完整的支持这种数据库，在程序中建立的任何数据库，任何类都可以访问。
	 * SQLite中，默认表的主键必须是“_id”。
	 * 
	 */
	public void SQLiteDatabase() {
//		使用SQLiteDatabase类进行SQLite操作。实际使用时，最好将数据库操作都封装到一个工具类中。
		Context context = null;
		SQLiteDatabase sqLiteDatabase = null;
//		1 打开或建立数据库
//		方法一：这种方法建立的数据库路径是/data/data/[package name]/database/
//		第1个参数传入数据库名
//		第2个参数设置数据库权限。MODE_PRIVATE表示数据库只能被当前程序（或和当前程序用户ID相同的其他程序）读写，MODE_WORLD_READABLE表示所有程序都可以读，MODE_WORLD_WRITEABLE表示所有程序都可以写。
//		第3个参数
		sqLiteDatabase = context.openOrCreateDatabase("DatabaseName", Context.MODE_PRIVATE, null);
		
//		方法二：通过工具将数据库建好，然后将数据库文件打包进程序。
		
		
//		2 执行SQL
//		此方法执行没有返回值的SQL语句
		sqLiteDatabase.execSQL("SQL语句");
		
//		此方法执行有返回值的SQL语句（SQLITE原生，效率高）
//		参数2：一个字符串数组，系统会用数组元素依次替换参数1中的问号。这种转义在使用时，只能用于原本应该是字符串出现的地方。
		sqLiteDatabase.rawQuery("SQL", new String[]{"SelectionArgs"});
		
//		此方法执行有返回值的SQL语句（效率较低）
//		参数2：要查询的字段，传入null表示查询所有字段。
//		参数3：WHERE条件（不包括WHERE关键字），传入null返回所有行。
//		参数4：一个字符串数组，系统会用数组元素依次替换参数3中的问号。这种转义在使用时，只能用于原本应该是字符串出现的地方。
		sqLiteDatabase.query("TableName", new String[]{"Columns"}, "Selection", new String[]{"SelectionArgs"}, "GroupBy", "Having", "OrderBy");
		
//		3 建表
		sqLiteDatabase.execSQL("CREAT TABLE tablename(columnname INTEGER PRIMARY KEY AUTOINCREMENT,columnname VARCHAR(50) NOT NULL, ...)");
		
//		4 插入记录
//		参数1：要插入数据的表名
//		参数2：填写一个可为空的字段名。当参数3为null，或其长度小于等于0时，insert是会失败的（底层数据库不允许插入一个空行）。所以要在这里指定一个可为null的字段名，就会将你指定的这个字段的值设为null，然后再向数据库中插入。
//		参数3：插入的字段名和字段值。ContentValues对象，类似一个map，通过键值对的形式存储。
		ContentValues values = new ContentValues();
		values.put("ColumnName", "Values");
		values.put("ColumnName", 200);
		sqLiteDatabase.insert("TableName", "NullableColumnName", values);
		
//		5 更新记录
//		参数2：更新的字段名和字段值。ContentValues对象，类似一个map，通过键值对的形式存储。
//		参数3：WHERE条件（不包括WHERE关键字），传入null更新所有行。
//		参数4：一个字符串数组，系统会用数组元素依次替换参数3中的问号。这种转义在使用时，只能用于原本应该是字符串出现的地方。
		sqLiteDatabase.update("TableName", values, "Selection", new String[]{"SelectionArgs"});
		
//		6 删除记录
//		参数2：WHERE条件（不包括WHERE关键字），传入null删除所有行。
//		参数3：一个字符串数组，系统会用数组元素依次替换参数3中的问号。这种转义在使用时，只能用于原本应该是字符串出现的地方。
		sqLiteDatabase.delete("TableName", "Selection", new String[]{"SelectionArgs"});
		
//		7 关闭数据库。
		sqLiteDatabase.close();
		sqLiteDatabase = null;
		
	}
	
	/**
	 * android.database.sqlite.SQLiteOpenHelper
	 * 用来创建数据库、管理数据库版本的帮助类。使用这个类，可以省去在数据库打开之前，判断数据库是否需要创建或更新的逻辑。
	 * 这个类使ContentProvider更容易实现“程序第一次运行时，推迟打开或升级数据库，缩短程序启动时间”。
	 * 
	 * 
	 */
	public void SQLiteOpenHelper() {
//		1 创建一个类继承于SQLiteOpenHelper
		class DatabaseOpenHelper extends SQLiteOpenHelper {
			
//			继承父类的构造
//			参数2：数据库名，传入null则将数据库创建在内存中。
//			参数4：数据库版本号
			public DatabaseOpenHelper(Context context, String name, CursorFactory factory, int version) {
				super(context, name, factory, version);
			}
			
//			数据库创建时，系统调用本方法。这里应该生成表、执行初始化脚本等。
			@Override
			public void onCreate(SQLiteDatabase db) {
				
			}

//			数据库需要升级时（根据版本号判断），系统调用本方法。这里应该执行数据库升级脚本。
			@Override
			public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
				if (oldVersion == 1 && newVersion ==4) {
					
				} else if (oldVersion == 2 && newVersion == 4) {
					
				} else if (oldVersion == 3 && newVersion == 4) {
					
				}
				
			}

//			打开数据库时，系统调用本方法。这个方法可以不实现。
			@Override
			public void onOpen(SQLiteDatabase db) {
				super.onOpen(db);
				
//				对数据库操作前，判断是否只读。
				if (db.isReadOnly()) {
					
				}
			}
		}
		
//		2 新建一个SQLiteOpenHelper实现类的对象。
//		参数2：数据库名，传入null则将数据库创建在内存中。
//		参数4：数据库版本号
		SQLiteOpenHelper dbOpenHelper = new DatabaseOpenHelper(this, "DBName", null, 1);
		
//		3 使用SQLiteOpenHelper对象得到数据库对象。根据情况，会返回新创建的、已经存在的或者升级后的数据库。
//		下面两个方法第一次调用时可能会比较耗时，所以要在主线程中调用。
		SQLiteDatabase sqLiteDatabase = dbOpenHelper.getReadableDatabase(); //一般情况下，返回一个可读可写的数据库对象；若出现问题（比如可用空间不足），则返回只读的数据库对象。
		dbOpenHelper.getWritableDatabase(); //返回一个可读可写的数据库对象；若出现问题（比如可用空间不足），则抛异常。
		
//		4 执行多个SQL时，使用事务操作
		sqLiteDatabase.beginTransaction(); //开始事务
		try {
			sqLiteDatabase.execSQL("insert into ...");
			sqLiteDatabase.execSQL("insert into ...");
			sqLiteDatabase.setTransactionSuccessful(); //设置事务执行成功
		} catch (Exception e) {
//			若有一条SQL执行失败，则整个事务都会回滚。所以不需要手工再处理。
		} finally {
			sqLiteDatabase.endTransaction(); //设置事务结束
		}
		
//		5 数据库使用完毕后，需要关闭。
		dbOpenHelper.close();
		
	}
	
	
	/**
	 * SharedPreferences
	 * android.content.SharedPreferences类提供对简单类型数据（boolean、float、int、long、string）的永久存储和读取。
	 * 存储的数据在整个用户会话过程中都不会丢失，即使程序被系统杀死。
	 * 存储数据的XML文件路径为：/data/data/[package name]/shared_prefs/[preferences filename].xml
	 * 
	 */
	public void SharedPreferences() {
//		存储数据的步骤
//		1 得到SharedPreferences对象
		SharedPreferences sharedPreferences;
		
//		若需要使用多个SharedPreferences文件存储数据，使用getSharedPreferences()方法。
//		第1个参数传入SharedPreferences文件名
//		第2个参数设置文件权限。MODE_PRIVATE表示文件只能被当前程序（或和当前程序用户ID相同的其他程序）读写，MODE_WORLD_READABLE表示所有程序都可以读，MODE_WORLD_WRITEABLE表示所有程序都可以写。
		sharedPreferences = this.getSharedPreferences("XMLFileName", Context.MODE_PRIVATE);
		
//		若只是对当前的Activity存储数据，可以使用getPreferences()方法。本方法使用当前的Activity名作为SharedPreferences文件名。
		sharedPreferences = this.getPreferences(Context.MODE_PRIVATE);
		
//		若需要设置整个程序使用的全局设置，应该使用PreferenceManager.getDefaultSharedPreferences()方法。本方法使用程序的包名作为SharedPreferences文件名。
		sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
		
		
		
//		2 得到SharedPreferences.Editor对象
		SharedPreferences.Editor editor = sharedPreferences.edit();
		
//		3 用putXxx()方法存储数据
		editor.putString("Key", "Value");
		editor.putBoolean("是否震动", true);
		editor.putInt("当前长度", 10);
		
//		4 用commit()方法提交改变
		editor.commit();
		
		
//		读取数据的步骤
//		1 得到SharedPreferences对象
//		同上
		
//		2 用getXxx()方法读取数据。第1个参数传入Key，第2个参数传入默认值。
		sharedPreferences.getString("Key", "Value");
		sharedPreferences.getBoolean("是否震动", true);
		sharedPreferences.getInt("当前长度", 10);
		Map<String, ?> map = sharedPreferences.getAll();//得到所有存储的数据，应该避免使用此方法。返回的map对象只能读不能写，并且其中得到的数据类型也无法确定了。
		
	}
	
	/**
	 * PreferenceActivity
	 * 本类是实现Activity向用户显示一系列的选项设置的基础类。
	 * Android 3.0之前只能使用本类显示一套单一的设置；Android 3.0之后，可以和PreferenceFragment类一起使用，将设置显示成框架式的。
	 * 同一个程序中，所有用PreferenceActivity存储的数据都放在同一个SharedPreferences文件中。
	 * 
	 */
	public void AboutPreferenceActivity() {
//		建立PreferenceActivity的步骤
//		1 建立PreferenceXML文件，注意文件路径是res/xml。
//		PreferenceXML文件类似于Layout文件，PreferenceActivity通过这个文件来生成界面。
		/*
		<PreferenceScreen /> - 设置页面，可嵌套形成二级设置页面。必须为整个文件的根元素。
	    <PreferenceCategory /> - 设置分组
	    <Preference /> - 就是一段显示文本，需要与其他标签组合使用。
	    <CheckBoxPreference /> - 复选框选项
	    <EditTextPreference /> - 文本框选项
	    <ListPreference /> - 列表选项
	    <RingtonePreference /> - 铃声选项（包含ringtone、notification、alarm）
	    <intent /> - 这个标签可以定义action，打开其他Activity（比如系统设置）
	    */
		
//		2 生成Activity，并继承于PreferenceActivity。
		class SettingActivity extends PreferenceActivity {

			@Override
			protected void onCreate(Bundle savedInstanceState) {
				super.onCreate(savedInstanceState);
				
//				调用addPreferencesFromResource()方法，将第一步的XML文件生成为页面。
//				this.addPreferencesFromResource(android.R.xml.class);
				
//				通过KEY找到对象的选项
				Preference preference = findPreference("Key");
				
//				选项值改变事件
				preference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
//					选项值被改变前的一刻，系统调用本方法。
					@Override
					public boolean onPreferenceChange(Preference preference, Object newValue) {
						return false; //想要用newValue更新选项值，返回true；想要选项值保持不变，返回false。
					}
				});
				
//				选项点击事件
				preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
					@Override
					public boolean onPreferenceClick(Preference preference) {
						return false; //点击被处理，返回true；没有处理，返回false。
					}
				});
				
				
			}

		}
		
	}
	
	/**
	 * FileStorage
	 * 文件可以存储的地方有两个：InternalStorage、ExternalStorage。
	 * 二者的区分也很简单，InternalStorage不能挂载，ExternalStorage可以挂载。
	 * 
	 * InternalStorage
	 * 存储在内部存储器的文件默认是私有的，其他程序和用户都不能访问。
	 * 用户卸载程序时，系统会将文件删除。
	 * 内部存储适合放体积小、敏感度高的文件。
	 * 
	 * ExternalStorage
	 * 每个Android设备都支持共享式的外部存储器，也可以存放文件。
	 * 外部存储器上的文件是谁都可以访问的（包括用户）。
	 * 内部存储适合放体积大、敏感度低的文件。
	 * 
	 * 
	 * 
	 */
	public void FileStorage() {
//		InternalStorage读写文件的步骤
		
//		1 调用openFileOutput()得到FileOutputStream对象。
//		第1个参数传入不包含路径的文件名
//		第2个参数设置文件权限。MODE_PRIVATE表示创建私有文件，若存在同名文件则覆盖；MODE_APPEND表示创建私有文件，若存在同名文件则追加；MODE_WORLD_READABLE表示所有程序都可以读，MODE_WORLD_WRITEABLE表示所有程序都可以写。
		try {
			FileOutputStream fileOutputStream = this.openFileOutput("文件名", Context.MODE_PRIVATE);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		
//		2 调用openFileInput()得到FileInputStream对象。
		try {
			FileInputStream fileInputStream = this.openFileInput("文件名");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		
//		3 使用FileOutputStream对象写入文件。（具体看J2SE的IO部分的笔记）
//		4 使用FileInputStream对象读取文件。（具体看J2SE的IO部分的笔记）
		
		
//		ExternalStorage读写文件的步骤
		
//		1 对外部存储器进行操作之前，总是要调用getExternalStorageState()来判断其状态
		String sdState = Environment.getExternalStorageState();
		if (Environment.MEDIA_REMOVED.equalsIgnoreCase(sdState)) {
//			ExternalStorage不存在
		} else if (Environment.MEDIA_CHECKING.equalsIgnoreCase(sdState)) {
//			ExternalStorage存在，但正在进行媒体扫描。
		} else if (Environment.MEDIA_SHARED.equalsIgnoreCase(sdState)) {
//			ExternalStorage存在，但处于大容量存储共享模式。
		} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equalsIgnoreCase(sdState)) {
//			ExternalStorage存在，并已挂载只读权限。
		} else if (Environment.MEDIA_MOUNTED.equalsIgnoreCase(sdState)) {
//			ExternalStorage存在，并已挂载读写权限。
		}
		
//		2 外部存储器上的文件目录
//		若是Android 2.2及以上版本，得到程序私有目录。程序被卸载后，系统会删除私有目录。
		getExternalFilesDir(null); //返回程序在外部存储器上的私有文件目录，路径是/Android/data/<package_name>/files/。
		getExternalFilesDir(Environment.DIRECTORY_PICTURES); //返回程序在外部存储器上的私有图片目录，路径是/Android/data/<package_name>/files/Pictures。
		getExternalCacheDir(); //返回程序在外部存储器上的私有缓存目录，路径是/Android/data/<package_name>/cache/。
		
//		若是Android 2.2及以上版本，得到共享目录。程序被卸载后，系统不会删除共享目录。
		Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); ////返回在外部存储器上的共享目录，路径是/Pictures/。
		
//		若是Android 2.1及以下版本，无法得到程序私有目录。
		Environment.getExternalStorageDirectory(); //返回外部存储器的根目录，需要手工将文件放到对应的私有目录下，这样程序被卸载时此目录才会被系统删除。
		
//		若是Android 2.1及以下版本，无法得到共享目录。
		Environment.getExternalStorageDirectory(); //返回外部存储器的根目录，需要手工将文件放到对应的共享目录下。
		
		
		
	}
	
	/**
	 * NetworkConnection
	 * 可以使用互联网在自有的服务器上存储和读取数据。Android中的网络操作，使用以下两个包中的类：
	 * java.net.*
	 * android.net.*
	 * 
	 */
	public void AboutNetworkConnection() {
		
	}
	
}
